home *** CD-ROM | disk | FTP | other *** search
/ Network Supervisor's Toolkit / Network Supervisor's Toolkit.iso / tools / pprd100 / pprd.c < prev    next >
C/C++ Source or Header  |  1996-07-10  |  20KB  |  807 lines

  1. /*
  2.  
  3. PPRD
  4.  
  5. Line printer daemon using Berkeley LPD protocol on top of WATTCP for up
  6. to 3 parallel printer ports.
  7.  
  8. This daemon is written as a state machine because it has to support
  9. several connections on a single threaded system, DOS.
  10.  
  11. When started up, it checks to see how many printers are known to the
  12. BIOS. These printers are served as LPT[123].  It listens on the
  13. standard LPD port 515, but this can be changed from the command line.
  14.  
  15. Examples of options:
  16.  
  17. -p1515    listen on port 1515 instead
  18. -j9200    use direct protocol instead at port 9100 (defaults to 9100 if unspec)
  19. -23    disable printers 2 and 3
  20. -n2    two printers, no matter what BIOS claims
  21. -b12    on 1 and 2 bypass BIOS and send directly to port (parallel ports only)
  22. -t    don't indicate available printers with tones
  23. -i    reinitialise printer via hardware line on job abort
  24. -s    disable subnet match (server and client must be on same subnet)
  25. -alist    comma separated list of up to 20 domain names allowed connection
  26. -dlist    comma separated list of up to 20 domain names denied connection
  27.     last two are mutually exclusive and independent of subnet check
  28. -lhost    log diagnostics to host of that domain name
  29.  
  30. Jobs can be aborted by C-F1 through C-F3 for that printer.
  31.  
  32. The LPD protocol is summarised below where S = server and C = client.
  33. Ack = '\0' and nak = '\001'.
  34.  
  35. C: \002printer\n
  36. S: ack/nak
  37. C: \003size datafilename\n    (size is bytes as decimal string)
  38. S: ack/nak
  39. C: size bytes of data + one '\0' byte
  40. S: ack/nak
  41. C: \002size cntlfilename\n
  42. S: ack/nak
  43. C: size bytes of control + one '\0' byte
  44. S: ack/nak
  45.  
  46. The data and control files can occur in either order.  In this
  47. implementation the filenames are ignored.
  48.  
  49. This program was compiled under Borland C++ 3.1 in C mode for the small
  50. model. You will require the WATTCP libraries. A copy is included in the
  51. distribution. For the sources of WATTCP, ask archie where the archives
  52. are.
  53.  
  54. Please send bug fixes, ports and enhancements to the author for
  55. incorporation in newer versions.
  56.  
  57. Copyright (C) 1995  Ken Yap (ken@syd.dit.csiro.au)
  58.  
  59. This program is free software; you can redistribute it and/or modify
  60. it under the terms of the GNU General Public License as published by
  61. the Free Software Foundation; either version 2 of the License, or
  62. (at your option) any later version.
  63.  
  64. This program is distributed in the hope that it will be useful,
  65. but WITHOUT ANY WARRANTY; without even the implied warranty of
  66. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  67. GNU General Public License for more details.
  68.  
  69. You should have received a copy of the GNU General Public License
  70. along with this program; if not, write to the Free Software
  71. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  72.  
  73. */
  74.  
  75. #include    <stdio.h>
  76. #include    <stdlib.h>
  77. #include    <string.h>
  78. #include    <memory.h>
  79. #include    <sys/types.h>
  80. #include    <time.h>
  81.  
  82. #include    <tcp.h>
  83.  
  84. #include    <dos.h>
  85. #include    <bios.h>
  86. #include    <conio.h>
  87.  
  88. #include    "pprd.h"
  89.  
  90. #define        PROGRAM        "PPRD"
  91. #define        VERSION        "Version 1.00 15 Jul 1995"
  92. #define        AUTHOR        "Copyright (C) 1995 Ken Yap (ken@syd.dit.csiro.au)"
  93.  
  94. #define        MAXLPT        3    /* supported under DOS */
  95. #ifdef        unix            /* test implementation */
  96. #define        MAXCON        1
  97. #else
  98. /* for lpr, 1 for lpq, and direct */
  99. #define        MAXCON        (MAXLPT+1)
  100. #endif
  101. /* try this many times to see if can accept next byte right away */
  102. #define        POLL_MAX    10
  103.  
  104. unsigned int        lpdport = LPDPORT;
  105. unsigned int        jdport = JDPORT;
  106. int            jdprotocol = 0;    /* use direct protocol if != 0 */
  107. int            nlpt = 0;
  108. struct lpt_info        lpt[MAXLPT];
  109. int            notone = 0;
  110. int            reinit = 0;    /* reinitialise on abort */
  111. unsigned int        lpt_tone[MAXLPT] = { 523, 587, 659 };    /* CDE */
  112. char            lpt_names[MAXLPT][16] = { "lpt1", "lpt2", "lpt3" };
  113. struct conn_info    conn[MAXCON];    /* one extra for lpq/lprm requests */
  114. struct conn_info    log;        /* for connection to syslogd */
  115. char            *loghost = 0;
  116. int            check_subnet = 1;
  117. int            nallow = 0, ndeny = 0;
  118. longword        allow[20], deny[20];
  119. void                (*normal_init)(char *name, char *value);
  120.  
  121. #define        MAXALLOW    (sizeof(allow)/sizeof(allow[0]))
  122. #define        MAXDENY        (sizeof(deny)/sizeof(deny[0]))
  123.  
  124. extern longword        my_ip_addr;
  125. extern longword        sin_mask;
  126.  
  127. #define    ack_cmd(c)    (void)sock_fastwrite(&c->sock, (byte *)"", 1)
  128. #define    nak_cmd(c)    (void)sock_fastwrite(&c->sock, (byte *)"\001", 1)
  129.  
  130. char *ptime(void)
  131. {
  132.     time_t    t;
  133.     char    *p;
  134.  
  135.     t = time(0);
  136.     p = ctime(&t);
  137.     p[24] = '\0';
  138.     return (p);
  139. }
  140.  
  141. void init_queues(void)
  142. {
  143.     struct conn_info    *c;
  144.     int            i, ret;
  145.     struct lpt_info        *p;
  146.  
  147.     if (nlpt == 0)                /* not overridden by -n? */
  148.     {
  149.         ret = biosequip();
  150.         nlpt = (ret >> 14) & 0x3;
  151.     }
  152.     for (i = 0; i < nlpt; ++i)
  153.     {
  154.         p = &lpt[i];
  155.         if (p->avail == DISABLED)    /* unavail from cmd line? */
  156.             continue;
  157.         (void)bios_printer_init(i);    /* initialise printer */
  158.         sleep(2);            /* let printer settle */
  159.         p->status = bios_printer_status(i);    /* get status */
  160.         if (p->status & (P_TIMEOUT|P_IOERROR))
  161.         {
  162.             p->avail = NONESUCH;    /* printer not available */
  163.             (void)printf("%s: Error initialising %s\n",
  164.                 ptime(), lpt_names[i]);
  165.         }
  166.         else
  167.         {
  168.             p->avail = FREE;    /* printer available */
  169.             (void)printf("%s: %s initialised",
  170.                 ptime(), lpt_names[i]);
  171.             if (p->hwaddr && (ret = peek(0, 0x408 + i * 2)) != 0)
  172.                 (void)printf(", direct hardware access at %#3X",
  173.                     p->hwaddr = ret);
  174.             (void)printf("\n");
  175.         }
  176.     }
  177.     for (i = 0; i < MAXLPT; ++i)
  178.     {
  179.         if (notone || lpt[i].avail != FREE)
  180.             continue;
  181.         sound(lpt_tone[i]);
  182.         sleep(1);
  183.         nosound();
  184.     }
  185.     for (i = 0; i < MAXCON; ++i)
  186.     {
  187.         c = &conn[i];
  188.         c->state = INIT;        /* initial connection state */
  189.         c->controlfirst = 0;
  190.         c->printer = -1;
  191.     }
  192. }
  193.  
  194. void init_log(void)
  195. {
  196.     longword        logip;
  197.  
  198.     if (loghost == 0 ||
  199.         (logip = resolve(loghost)) == (longword)0 ||
  200.         udp_open(&log.sock, 0, logip, LOGPORT, NULL) == 0)
  201.     {
  202.         loghost = 0;
  203.         return;
  204.     }
  205.     (void)sprintf(log.buffer, LOG_TAG PROGRAM " " VERSION
  206.         ", %d printer(s)\n", nlpt);
  207.     (void)sock_fastwrite(&log.sock, log.buffer, strlen(log.buffer));
  208. }
  209.  
  210. void report_change(char *msg, int printer)
  211. {
  212.     (void)printf("%s: %s %s\n", ptime(), lpt_names[printer], msg);
  213.     if (loghost != 0)        /* log to syslogd */
  214.     {
  215.         (void)sprintf(log.buffer, LOG_TAG "%s %s\n", lpt_names[printer], msg);
  216.         (void)sock_fastwrite(&log.sock, log.buffer, strlen(log.buffer));
  217.     }
  218. }
  219.  
  220. void lpt_status_change(int i, int delta, int status)
  221. {
  222.     if (status & P_TIMEOUT)
  223.         report_change("time out", i);
  224.     if (status & P_IOERROR)
  225.         report_change("I/O error", i);
  226.     if (delta & P_SELECTED)
  227.     {
  228.         if (status & P_SELECTED)
  229.             report_change("online", i);
  230.         else
  231.             report_change("offline", i);
  232.     }
  233.     if (status & P_NOPAPER)
  234.         report_change("paper out", i);
  235. }
  236.  
  237. int same_subnet(longword client)
  238. {
  239.     /* assumes that bit operations can be done on longword */
  240.     return (((client ^ my_ip_addr) & sin_mask) == 0);
  241. }
  242.  
  243. int in_list(longword ip, longword table[], int entries, int maxtable)
  244. {
  245.     int        i;
  246.  
  247.     for (i = 0; i < entries && i < maxtable; ++i)
  248.         if (ip == table[i])
  249.             return (1);
  250.     return (0);
  251. }
  252.  
  253. int check_access(longword ip)
  254. {
  255.     if (check_subnet && !same_subnet(ip))
  256.         return (0);
  257.     if (nallow > 0)
  258.         return (in_list(ip, allow, nallow, MAXALLOW) ? 1 : 0);
  259.     if (ndeny > 0)
  260.         return (in_list(ip, deny, ndeny, MAXDENY) ? 0 : 1);
  261.     return (1);
  262. }
  263.  
  264. /*
  265.  *    Translate from name to printer number
  266.  */
  267. int printernumber(char *printername)
  268. {
  269.     int        i;
  270.  
  271.     for (i = 0; i < MAXLPT; ++i)
  272.         if (stricmp(lpt_names[i], printername) == 0)
  273.             return (i);
  274.     return (-1);
  275. }
  276.  
  277. void queuename(struct conn_info *c)
  278. {
  279.     int        i, current, delta;
  280.     char        printername[sizeof(lpt_names[0])];
  281.  
  282.     *c->bufip = '\0';
  283.     if (*c->buffer != '\002' && *c->buffer != '\003')
  284.     {
  285.         c->state = NAKANDCLOSE;
  286.         return;
  287.     }
  288.     if (sscanf(c->buffer+1, "%15s", printername) != 1 ||
  289.         (i = printernumber(printername)) < 0)
  290.     {
  291.         (void)printf("%s: Printer name error: %s", ptime(), c->buffer+1);
  292.         c->state = NAKANDCLOSE;
  293.         return;
  294.     }
  295.     switch (*c->buffer)
  296.     {
  297.     case '\002':
  298.         if (lpt[i].avail != FREE)
  299.         {
  300.             (void)printf("%s: Printer not available: %s",
  301.                 ptime(), c->buffer+1);
  302.             c->state = NAKANDCLOSE;
  303.             return;
  304.         }
  305.         c->printer = i;
  306.         lpt[i].avail = BUSY;
  307.         (void)printf("%s: Job for %s started\n",
  308.             ptime(), lpt_names[i]);
  309.         c->state = RECVJOB;
  310.         ack_cmd(c);
  311.         break;
  312.     case '\003':
  313.     case '\004':
  314.         (void)sprintf(c->buffer, "%s is ", lpt_names[i]);
  315.         switch (lpt[i].avail)
  316.         {
  317.         case BUSY:
  318.             (void)strcat(c->buffer, "busy\n");
  319.             break;
  320.         case FREE:
  321.             (void)strcat(c->buffer, "available");
  322.             /* get status of printer and save it */
  323.             lpt[i].status = current = bios_printer_status(i);
  324.             (void)strcat(c->buffer, current & P_SELECTED ?
  325.                 " online" : " offline");
  326.             if (current & P_NOPAPER)
  327.                 (void)strcat(c->buffer, " no paper");
  328.             (void)strcat(c->buffer, "\n");
  329.             break;
  330.         default:
  331.             (void)strcat(c->buffer, "not available\n");
  332.             break;
  333.         }
  334.         (void)sock_fastwrite(&c->sock, c->buffer, strlen(c->buffer));
  335.         c->state = CLOSING;
  336.         break;
  337.     }
  338. }
  339.  
  340. void get_jobinfo(struct conn_info *c)
  341. {
  342.     char            *p;
  343.  
  344.     p = c->bufip;
  345.     if (buffer_room(c) == 0)
  346.         --p;
  347.     *p = '\0';
  348.     for (p = strtok(c->buffer, "\n"); p != 0; p = strtok(0, "\n"))
  349.     {
  350.         switch (*p)
  351.         {
  352.         case 'N':
  353.             (void)strncat(c->jobname, p+1, sizeof(c->jobname)-1);
  354.             break;
  355.         case 'P':
  356.             (void)strncat(c->username, p+1, sizeof(c->username)-1);
  357.             break;
  358.         case 'H':
  359.             (void)strncat(c->hostname, p+1, sizeof(c->hostname)-1);
  360.             break;
  361.         }
  362.     }
  363. }
  364.  
  365. void read_bytes(struct conn_info *c)
  366. {
  367.     int        count;
  368.     char        *p;
  369.  
  370.     if ((count = sock_rbused(&c->sock)) > 0)
  371.     {
  372.         if (count > buffer_room(c))
  373.             count = buffer_room(c);
  374.         (void)sock_fastread(&c->sock, (byte *)c->bufip, count);
  375.         c->bufip += count;
  376.     }
  377.     if (!data_unseen(c))
  378.         return;
  379. nextstate:
  380.     switch (c->state)
  381.     {
  382.     case QUEUENAME:
  383.         queuename(c);
  384.         reset_ptrs(c);
  385.         break;
  386.     case RECVJOB:
  387.         switch (*c->buffer)
  388.         {
  389.         case '\001':
  390.             ack_cmd(c);
  391.             c->state = CLOSING;
  392.             break;
  393.         case '\002':
  394.             c->state = CONTROLINFO;
  395.             c->controlfirst = 1;
  396.             goto nextstate;
  397.         case '\003':
  398.             c->state = DATAINFO;
  399.             goto nextstate;
  400.         }
  401.     case CONTROLINFO:
  402.     case DATAINFO:
  403.         p = c->bufip;
  404.         if (buffer_room(c) == 0)
  405.             --p;
  406.         *p = '\0';
  407.         if (sscanf(c->buffer+1, "%ld", &c->bytelen) != 1)
  408.         {
  409.             (void)printf("%s: %s, error parsing %s",
  410.                 ptime(), lpt_names[c->printer], c->buffer+1);
  411.             c->state = NAKANDCLOSE;
  412.             return;
  413.         }
  414.         ++c->bytelen;        /* for EOF byte */
  415.         ack_cmd(c);
  416.         c->jobname[0] = '\0';
  417.         c->username[0] = '\0';
  418.         c->hostname[0] = '\0';
  419.         c->state = c->state == CONTROLINFO ? CONTROL : DATA;
  420.         if (c->state == DATA)
  421.             c->starttime = time(0);
  422.         reset_ptrs(c);
  423.         break;
  424.     case CONTROL:
  425.         /* copy out interesting information from buffer */
  426.         get_jobinfo(c);
  427.         c->bytelen -= count;
  428.         if (c->bytelen <= 0)
  429.         {
  430.             (void)printf("%s: Job ", ptime());
  431.             if (c->jobname[0] != '\0')
  432.                 (void)printf("%s ", c->jobname);
  433.             if (c->username[0] != '\0' && c->hostname[0] != '\0')
  434.                 (void)printf("for %s@%s ", c->username,
  435.                     c->hostname);
  436.             (void)printf("on %s\n", lpt_names[c->printer]);
  437.             ack_cmd(c);
  438.             c->state = c->controlfirst ? DATAINFO : CLOSING;
  439.         }
  440.         reset_ptrs(c);
  441.         break;
  442.     case DATA:
  443.         c->bytelen -= count;
  444.         if (c->bytelen <= 0 && count > 0)    /* skip EOF byte */
  445.             --c->bufip;
  446.         c->state = PRINTING;
  447.         break;
  448.     }
  449. }
  450.  
  451. /*
  452.  *    Direct hardware write to printer port
  453.  */
  454. int printer_outbuf(int port, char *buffer, int count)
  455. {
  456.     int        i, printed, status;
  457.  
  458.     printed = 0;
  459.     inportb(port+1);        /* read status */
  460.     status = (inportb(port+1) & 0xf8) ^ 0x48;
  461.     while (count > 0 && (status & P_READY) == P_READY)
  462.     {
  463.         outportb(port, *buffer++);    /* write character */
  464.         outportb(port+2, 0x0d);        /* raise strobe */
  465.         outportb(port+2, 0x0c);        /* lower strobe */
  466.         ++printed;
  467.         --count;
  468.         /* sample the busy line for a few tries */
  469.         for (i = 0; i < POLL_MAX; ++i)
  470.         {
  471.             status = (inportb(port+1) & 0xf8) ^ 0x48;
  472.             if ((status & P_READY) == P_READY)
  473.                 break;
  474.         }
  475.     }
  476.     return (printed);
  477. }
  478.  
  479. void print_data(struct conn_info *c)
  480. {
  481.     int        printer, current, delta, printed;
  482.     struct lpt_info    *p;
  483.  
  484.     p = &lpt[printer = c->printer];
  485.     current = bios_printer_status(printer);
  486.     /* report changes from last time */
  487.     delta = (current ^ p->status) & P_CHANGES;
  488.     if (delta)
  489.         lpt_status_change(printer, delta, current);
  490.     p->status = current;
  491.     /* if printer is busy, try to collect more data */
  492.     if ((current & P_READY) != P_READY && buffer_room(c) > 0)
  493.     {
  494.         c->state = DATA;
  495.         return;
  496.     }
  497.     if (p->hwaddr != 0)
  498.     {
  499.         printed = printer_outbuf(p->hwaddr, c->bufop,
  500.             c->bufip - c->bufop);
  501.         c->bufop += printed;
  502.     }
  503.     else
  504.     {
  505.         printed = 0;
  506.         /* loop, printing as much as possible */
  507.         /* eat your heart out, Pascal */
  508.         while (c->bufop < c->bufip &&
  509.             ((current & P_READY) == P_READY ||
  510.             (bios_printer_status(printer) & P_READY) == P_READY))
  511.         {
  512.             current = bios_printer_outch(printer, *c->bufop++);
  513.             ++printed;
  514.         }
  515.     }
  516.     if (!data_remaining(c))
  517.     {
  518.         reset_ptrs(c);
  519.         /* in jdprotocol bytelen will not be 0 before EOF
  520.            well, not before maxlong bytes anyway */
  521.         if (c->bytelen > 0)
  522.             c->state = DATA;
  523.         else
  524.         {
  525.             ack_cmd(c);
  526.             c->state = c->controlfirst ? CLOSING : CONTROLINFO;
  527.         }
  528.     }
  529.     c->joblen += printed;
  530. }
  531.  
  532. void show_stats(struct conn_info *c)
  533. {
  534.     time_t        elapsed;
  535.  
  536.     elapsed = time(0) - c->starttime;
  537.     (void)printf("%s: %s: %ld bytes %ld seconds", ptime(),
  538.         lpt_names[c->printer], c->joblen, elapsed);
  539.     if (elapsed > 0)
  540.         (void)printf(" %ld bytes/second", c->joblen / elapsed);
  541.     (void)printf("\n");
  542. }
  543.  
  544. void loop(int port, struct conn_info *c)
  545. {
  546.     unsigned int    cport;
  547.     struct sockaddr    client;
  548.     int        i;
  549.     char        *name;
  550.  
  551.     switch (c->state) {
  552.     case INIT:
  553.         if (jdprotocol)
  554.         {
  555.             if (port >= MAXLPT || lpt[port].avail != FREE)
  556.                 break;        /* don't offer connection */
  557.             cport = jdport + port;
  558.             c->printer = port;
  559.         }
  560.         else
  561.             cport = lpdport;
  562.         tcp_listen(&c->sock, cport, 0L, 0, NULL, 0);
  563.         (void)printf("%s: Connection %d listening on TCP port %u\n",
  564.             ptime(), port, cport);
  565.         c->state = WAITING;
  566.         break;
  567.     case WAITING:
  568.         tcp_tick(NULL);
  569.         if (!sock_established(&c->sock))
  570.             break;
  571.         i = sizeof(client);
  572.         client.s_ip = 0;
  573.         name = getpeername(&c->sock, &client, &i) == 0 ?
  574.             inet_ntoa(c->buffer, client.s_ip) : "?";
  575.         if (check_access(client.s_ip))
  576.         {
  577.             (void)printf("%s: Connection %d from %s\n",
  578.                 ptime(), port, name);
  579.             c->state = jdprotocol ? DATA : QUEUENAME;
  580.             c->bytelen = 0x7ffffff;    /* maxlong */
  581.             c->starttime = time(0);
  582.             c->joblen = 0L;
  583.         }
  584.         else
  585.         {
  586.             (void)printf("%s: Connection %d from %s refused\n",
  587.                 ptime(), port, name);
  588.             c->state = CLOSING;
  589.         }
  590.         reset_ptrs(c);
  591.         break;
  592.     case QUEUENAME:
  593.     case RECVJOB:
  594.     case CONTROLINFO:
  595.     case CONTROL:
  596.     case DATAINFO:
  597.     case DATA:
  598.         if (tcp_tick(&c->sock))
  599.             read_bytes(c);
  600.         /* don't shutdown until all printed */
  601.         else if (data_remaining(c))
  602.             c->state = PRINTING;
  603.         else
  604.             c->state = CLOSING;
  605.         break;
  606.     case PRINTING:
  607.         if (tcp_tick(&c->sock) || data_remaining(c))
  608.             print_data(c);
  609.         else
  610.             c->state = CLOSING;
  611.         break;
  612.     case NAKANDCLOSE:
  613.         nak_cmd(c);
  614.     case CLOSING:
  615.         show_stats(c);
  616.         sock_flush(&c->sock);
  617.         sock_close(&c->sock);
  618. reinit:
  619.         /* free up printer */
  620.         if (0 <= c->printer && c->printer < MAXLPT
  621.             && lpt[c->printer].avail == BUSY)
  622.             lpt[c->printer].avail = FREE;
  623.         c->state = INIT;
  624.         c->controlfirst = 0;
  625.         c->printer = -1;
  626.         break;
  627.     case ABORT:
  628.         sock_abort(&c->sock);
  629.         goto reinit;
  630.     }
  631. }
  632.  
  633. void check_key(void)
  634. {
  635.     int        key, i;
  636.  
  637.     if (((key = bioskey(0)) & 0xff) != 0)
  638.         return;            /* ASCII key */
  639.     key = (key >> 8) & 0xff;
  640.     if (key < CF1 || key > CF3)
  641.         return;
  642.     key -= CF1;        /* which printer? */
  643.     for (i = 0; i < MAXCON; ++i)
  644.     {
  645.         if (conn[i].printer == key && conn[i].state != INIT)
  646.         {
  647.             if (reinit)
  648.             {
  649.                 (void)printf("%s: Aborting job and reinitialising %s\n",
  650.                     ptime(), lpt_names[key]);
  651.                 (void)biosprint(1, 0, key);
  652.                 sleep(2);        /* let printer settle */
  653.             }
  654.             else
  655.             {
  656.                 (void)printf("%s: Aborting job on %s\n",
  657.                     ptime(), lpt_names[key]);
  658.             }
  659.             conn[i].state = ABORT;        /* abort job */
  660.             return;
  661.         }
  662.     }
  663. }
  664.  
  665. void make_list(char *adjective, char *s,
  666.     longword table[], int *nentries, int maxtable)
  667. {
  668.     char        *p;
  669.     longword    ip;
  670.     int        i;
  671.     char        buffer[64];
  672.  
  673.     for ( ; *s != '\0'; s = p + 1)
  674.     {
  675.         if ((p = strchr(s, ',')) == 0)
  676.             p = s + strlen(s) - 1;
  677.         else
  678.             *p = '\0';        /* mark end */
  679.         if ((ip = resolve(s)) == (longword)0)
  680.             continue;
  681.         if (*nentries >= maxtable)
  682.             continue;        /* should print warning */
  683.         table[(*nentries)++] = ip;
  684.     }
  685.     for (i = 0; i < *nentries; ++i)
  686.         (void)printf("%s ", inet_ntoa(buffer, table[i]));
  687.     if (*nentries > 0)
  688.         (void)printf("%s access\n", adjective);
  689. }
  690.  
  691. void my_init(char *name, char *value)
  692. {
  693.     if (strcmp(name, "PRINTER1NAME") == 0)
  694.     {
  695.         strncpy(lpt_names[0], value, sizeof(lpt_names[0]));
  696.         lpt_names[0][sizeof(lpt_names[0])-1] = '\0';
  697.     }
  698.     else if (strcmp(name, "PRINTER2NAME") == 0)
  699.     {
  700.         strncpy(lpt_names[1], value, sizeof(lpt_names[1]));
  701.         lpt_names[1][sizeof(lpt_names[1])-1] = '\0';
  702.     }
  703.     else if (strcmp(name, "PRINTER3NAME") == 0)
  704.     {
  705.         strncpy(lpt_names[2], value, sizeof(lpt_names[2]));
  706.         lpt_names[2][sizeof(lpt_names[2])-1] = '\0';
  707.     }
  708.     else if (normal_init)
  709.         (*normal_init)(name, value);
  710. }
  711.  
  712. void options(int argc, char **argv)
  713. {
  714.     char        *s;
  715.     int        i;
  716.  
  717.     for (i = 0; i < MAXLPT; ++i)
  718.         lpt[i].hwaddr = 0;
  719.     for (--argc, ++argv; argc > 0 && *argv[0] == '-'; --argc, ++argv)
  720.     {
  721.         s = argv[0];
  722.         switch (*++s)
  723.         {
  724.         case '1': case '2': case '3':
  725.             for ( ; '1' <= *s && *s <= '3'; ++s)
  726.                 lpt[*s - '1'].avail  = DISABLED;
  727.             break;
  728.         case 'a':
  729.             if (ndeny <= 0)
  730.                 make_list("allowed", ++s, allow, &nallow,
  731.                     MAXALLOW);
  732.             break;
  733.         case 'b':
  734.             ++s;
  735.             for ( ; '1' <= *s && *s <= '3'; ++s)
  736.                 lpt[*s - '1'].hwaddr = 1;
  737.             break;
  738.         case 'd':
  739.             if (nallow <= 0)
  740.                 make_list("denied", ++s, deny, &ndeny,
  741.                     MAXDENY);
  742.             break;
  743.         case 'i':
  744.             reinit = 1;
  745.             break;
  746.         case 'j':
  747.             jdprotocol = 1;
  748.             if ((jdport = atoi(++s)) <= 0)
  749.                 jdport = JDPORT;
  750.             break;
  751.         case 'l':
  752.             loghost = ++s;
  753.             break;
  754.         case 'n':
  755.             if ((nlpt = atoi(++s)) > MAXLPT || nlpt < 0)
  756.                 nlpt = 0;
  757.             break;
  758.         case 'p':        /* alternate TCP port */
  759.             if ((lpdport = atoi(++s)) <= 0)
  760.                 lpdport = LPDPORT;
  761.             break;
  762.         case 's':
  763.             check_subnet = 0;
  764.             break;
  765.         case 't':
  766.             notone = 1;
  767.             break;
  768.         }
  769.     }
  770. }
  771.  
  772. int main(int argc, char **argv)
  773. {
  774.     int        i;
  775.  
  776.     (void)printf(PROGRAM " " VERSION " " AUTHOR "\n");
  777.     (void)printf(PROGRAM " comes with ABSOLUTELY NO WARRANTY; for details read the file COPYING\n");
  778.     (void)printf("This is free software, and you are welcome to redistribute it\n");
  779.     (void)printf(" under certain conditions; see the file COPYING for details.\n");
  780.     (void)printf("C-F1 through C-F3 to abort printer jobs\n");
  781.     (void)memset(conn, 0, sizeof(conn));
  782.     (void)memset(lpt, 0, sizeof(lpt));
  783.     tzset();
  784.     options(argc, argv);
  785.     /* hook onto init procedure to get NAME=VALUE pairs */
  786.     normal_init = usr_init;
  787.     usr_init = my_init;
  788.     dbuginit();
  789.     sock_init();
  790.     init_queues();
  791.     if (nlpt == 0)
  792.     {
  793.         (void)printf("%s: No printers connected\n", ptime());
  794.         return (1);
  795.     }
  796.     init_log();
  797.     for (;;)
  798.     {
  799.         for (i = 0; i < MAXCON; ++i)
  800.             loop(i, &conn[i]);
  801.         if (kbhit())
  802.             check_key();
  803.     }
  804.     /*NOTREACHED*/
  805.     return (0);
  806. }
  807.